javascript 作用域与闭包

时间:

[TOC]

第一章 作用域是什么

js编译原理

  1. 到底是什么类型的语言?
    动态、解释型语言?
    no,它是编译型的语言
    why?

    不是提前编译的,编译在执行之前的几微妙甚至更短
    编译结果不能移植

  1. 编译的过程

    词法分析->语法分析->代码生成

理解作用域

  1. js的执行参与者

    • js引擎(v8、xx):负责编译和执行的过程
    • 编译器:词法分析、语法分析、代码生成
    • 作用域:收集和维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限
  2. code

    1
    var a=2;

    词法分析: var a = 2
    语法分析:var a; 作用域是否有a?有忽略,没有声明后放到作用域中
    生成代码:a=2;
    执行:js引擎会询问作用域有没有a,然后再赋值,如果没有就异常了。

  3. code 2

    1
    2
    3
    4
    5
    function foo(a){
    console.log(a+b);
    b=a;
    }
    foo(2);

    ReferenceError异常

  4. LHS和RHS

    LHS:查找赋值操作的目标是谁? a=
    RHS:查找谁是赋值操作的源头 console.log(a) 或者 =a

  5. function的LHS和RHS

    1
    2
    3
    4
    5
    function foo(a){
    console.log(a+b);
    b=a;
    }
    foo(2);

    LHS: foo(.. a.. b=..
    RHS: foo.. console.. log… a b a


不具有一般意义的一些代码

1
2
3
4
5
var foo = function(a){
console.log(a+b);
b=a;
}
foo(2);

LHS:foo.. foo(.. a.. b..
RHS:function(.. console.. …

注意:上面的LHS和RHS对于最后一个就不对了,编译器可以在代码生成时同时处理声明和值的定义。


  • 数一数

    1
    2
    3
    4
    5
     function foo(a){
    var b=a;
    return a+b;
    }
    var c= foo(2);

    LHS:3
    RHS:4

    作用域嵌套

  1. 作用域的嵌套

    向上找一直到全局作用域

异常

  1. LHS和RHS在嵌套时的不同

    LHS:如果到最后没有找到,那就创建一个(非严格模式)或者抛一个异常(ReferenceError,严格模式);
    RHS:如果到最后没有找到,那就抛一个异常(ReferenceError)
    可以理解code2的代码

小结

  • javascript的编译过程包括:词法分析->语法分析->代码生成
  • 作用域可以理解成一套规则,这套规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量的查找
  • 变量查找方法:LHS和RHS
  • 作用域的嵌套

第二章 词法作用域

作用域的工作模型:

  • 词法作用域
  • 动态作用域(Bash Perl)

    词法作用域

    定义与举例

    词法作用域是指定义在词法阶段的作用域。

    说人话:词法作用域就是你在写代码时定义在哪,就是哪个的作用域

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
    function foo(a) {
var b=a*2;
function bar(c) {
console.log(a,b,c);
}
bar(b*3);
}
foo(2);
console.log(a);
console.log(b);
console.log(c);
>
  1. 输出结果是什么?
  2. LHS和RHS分别有哪些?
  3. 作用域有几个?

查找

  • 遮蔽效应(全局如果被遮蔽,可以window.a访问)
  • 词法作用域只由函数声明时的所处的位置决定
  • 词法作用域查找只会查找一级标识符(对象的属性不会查找)

如何欺骗词法作用域—一定不要使用,只是为了了解

不管怎么说,都不要用
讲的目的只是为了看懂别人这么写的代码

在执行时,动态的修改词法作用域

####eval

功能:接收一个字符串作为参数,并将其中的内容看成在写代码时就加进去的一样

1
2
3
4
5
6
function foo(str, a) {
eval(str);
console.log(a, b);
}
var b = 2;
foo("var b=3;", 1);//1,3

补充:

实际中可以执行一段代码后再传str

严格模式: eval会有自己的作用域,不会修改插入的作用域了

其它:

setTimeOut(..) setInterval(..),第一个参数可以接受字符串
new Function(..) 最后一个参数接受代码字符串

不管怎么说,都不要用
讲的目的只是为了看懂别人这么写的代码

####with
with 会根据你传递给他的对象中凭空创造一个新的词法作用域

  1. 正常写法

    1
    2
    3
    4
    5
    6
    7
    8
    var obj = {
    a: 1,
    b: 2,
    c: 3
    }
    console.log(obj.a);
    console.log(obj.b);
    console.log(obj.c);
  2. 简单写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
        var obj = {
    a: 1,
    b: 2,
    c: 3
    }
    with(obj) {
    console.log(a);
    console.log(b);
    console.log(c);
    }
    ```

    3. 所带来的问题

    ```javacript

    function test(obj) {

    with(obj) {
        a = 2;
    }
    

    }
    var obj1 = {

    a: 1
    

    }
    var obj2 = {

    b: 1
    

    }
    test(obj1);
    console.log(obj1.a);
    test(obj2);
    console.log(obj2.a);
    console.log(a);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    >缺点:
    * 欺骗词法作用域会导致不可预知的结果(对于不懂的人来说)
    * 严格模式不支持
    * 性能

    **不管怎么说,都不要用**
    *讲的目的只是为了看懂别人这么写的代码*
    ###小结
    > * 定义
    * 欺骗词法作用域 **千万不要使用**

    ### 补充知识
    > 动态作用域
    >> 不关心在何处声明,只关心在何处调用
    >> ```javascript

    function test1() {

    console.log(a);
    

    }
    function bar() {

    var a = 3;
    test1();
    

    }
    var a = 2;
    bar();

    1
    2
    3
    4
    5
    6
    7
    8
    9

    ## 函数作用域和块作用域
    ### 函数
    ## 提升
    ## 闭包

    ## 看代码说结果
    ####代码1:
    ```javascript

    var a = 1;
    function foo() {

    var b = 2;
    function bar() {
        var b = 'bar2';
        console.log(b);
        function baz() {
            console.log(a);
        }
        baz();
    }
    bar();
    console.log(b);
    

    }
    foo();

    1
    2
    3
    4
    5
    6
        bar2
    1
    2

    ####代码2:
    ```javascript

    a = 2;
    var a;
    console.log( a );

    1
    2
    3
        2
    ####代码3:
    ```javascript

    console.log( a );
    var a = 2;

    1
    2
    3
        undefined  
    ####代码4:
    ```javascript

    foo();
    function foo() {

    console.log('foo');
    

    }

    1
    2
    3
        foo
    ####代码5:
    ```javascript

    //foo();
    bar();
    var foo = function bar() {

    console.log('foo');
    

    };

    1
    2
    3
    4
    5
    6
        foo is not function
    bar is not defined


    ####代码6:
    ```javascript

    var foo;
    function foo() { console.log( “a” ); }
    function foo() { console.log( “b” ); }

    foo();
    foo = true;
    if (foo) {

    }
    else {

    }
    foo();

    1
    2
    3
    4
    5
        b
    foo is not a function

    ####代码7:
    ```javascript

    var i;
    for (i=1; i<= 5; i++) {

    setTimeout( function timer(){
        console.log( i );
    }, i*1000);
    

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
        5
    5
    5
    5
    5
    ####代码8:
    var baz;
    function foo() {
    var a;
    function bar() {
    console.log( a );
    }
    a = 2;
    return bar;
    }
    baz = foo();
    baz();
    ```javascript

    function foo(j){

     function timer(){
         console.log(j);
     }
    return timer();
    

    }

    for (var i=1; i<=5; i++) {

    setTimeout(foo(i), i*1000 );
    

    }

    1
    2
    3
    4
    5
    6
    7
        1
    2
    3
    4
    5
    ####代码9:
    ```javascript

    function foo() {

    var a = 2;
    function bar() {
        console.log( a );
    }
    return bar;
    

    }
    var baz = foo();
    baz();

    1
    2
    3
        2
    ####代码10:
    ```javascript

    function foo(a){

    console.log(a+b);
    b=a;
    

    }
    foo(2);

    1
    2
    3
    4
       b is not defined

    ####代码11:
    ```javascript

    function foo(a){

    window.b=a;
    console.log(a+b);
    

    }
    a=2;
    foo(2);

    1
    2
    3
        4
    ####代码12:
    ```javascript

    var foo = function(a){

        console.log(a+b);
        b=a;
    }
    foo(2);
    
    1
    2
    ####代码13:
    ```javascript

    function foo(a) {

        var b=a*2;
        function bar(c) {
            console.log(a,b,c);
        }
        bar(b*3);
    }
    foo(2);
    console.log(a);
    console.log(b);
    console.log(c);
    
    1
    2
    3
    4
        2,4,12
    a is not defined;
    ####代码14:
    ```javascript

    function foo(str, a) {

    eval(str);
    console.log(a, b);
    

    }
    var b = 2;
    foo(“var b=3;”, 1);

    1
    2
    3
        1,3
    ####代码15:
    ```javascript

    function test(obj) {

    with(obj) {
        a = 2;
    }
    

    }
    var obj1 = {

    a: 1
    

    }
    var obj2 = {

    b: 1
    

    }
    test(obj1);
    console.log(obj1.a);
    test(obj2);
    console.log(obj2.a);
    console.log(a);

    1
    2
    3
    4
    5
    6
        2
    undefined
    2
    ####代码16:
    词法作用域
    ```javascript

    function test1() {

    console.log(a);
    

    }
    function bar() {

    var a = 3;
    test1();
    

    }
    var a = 2;
    bar();

    1
    2
    3
       2
    ####代码17:
    ```javascript

    function Person(name){

    this.name=name;
    this.fn=function(){
        alert(this.name);
    }
    

    }
    var person1=new Person(‘Byron’);
    console.log(person1.constructor==Person);
    console.log(person1 instanceof Person);

    1
    2
    3
    4
        true
    true
    ####代码18:
    ```javascript

    function Person(name){

    this.name=name;
    this.fn=function(){
        alert(this.name);
    }
    

    }
    var person1=new Person(‘Byron’);
    var person2=new Person(‘Frank’);
    console.log(person1.fn==person2.fn);

    1
    2
    ####代码19:
    ```javascript

    function Person(name){

    this.name=name;
    

    }
    Person.prototype.share=[];
    Person.prototype.printName=function(){

    alert(this.name);
    

    }
    var person1=new Person(‘Byron’);
    var person2=new Person(‘Frank’);
    console.log(person1.printName==person2.printName);

    1
    2
    ####代码20:
    ```javascript

    var myObj = {

    get a() {
        return this.h;
    },
    set a(val){
        this.h=val*2;
    }
    

    }
    Object.defineProperty(myObj, “b”, {

    get: function() {
        return 3;
        //enumerable: true
    }
    

    });
    myObj.a=2;
    console.log(myObj.a);
    console.log(myObj.b);
    myObj.hasOwnProperty(“a”);

    1
    2
    ####代码21:
    ```javascript

    var oneObj={

    a:2
    

    }
    var twoObj=Object.create(oneObj);
    console.log(twoObj.a);

    1
    2
    ####代码22:
    ```javascript

    function Test1(str) {

    this.a = str;
    

    }
    var myTest = new Test1(“test1”);
    console.log(myTest.a);
    function Test1WithoutNew(str) {

    this.a = str;
    

    }
    var myTestWithoutNew = Test1WithoutNew(“test1”);
    console.log(myTestWithoutNew.a);

    1
    2
    ####代码23:
    ```javascript

    function Test1(str) {

    this.a = str;
    return this.a;
    

    }
    var myTest = new Test1(“test1”);
    console.log(myTest);
    function Test1WithoutNew(str) {

    this.a = str;
    return this.a;
    

    }
    var myTestWithoutNew = Test1WithoutNew(“test1”);
    console.log(myTestWithoutNew);

    1
    2
    ####代码24:
    ```javascript

    function Test1(str) {

    this.a = str;
    return new String(this.a);
    

    }
    var myTest = new Test1(“test1”);
    console.log(myTest);
    ```
    test1

    闭包的作用(如何实现模块化

对象

函数

“类”与面向对象

原型

行为委托